/*
 * @(#)AbstractRetrieveService.java
 * Date 2013-10-25
 * Version 1.0
 * Author Jim Horner
 * Copyright (c)2012
 */


package us.hornerscorners.lamppost.xca.service;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import ihe.iti.xds_b._2007.RetrieveDocumentSetRequestType;
import ihe.iti.xds_b._2007.RetrieveDocumentSetResponseType;

import oasis.names.tc.ebxml_regrep.xsd.rs._3.RegistryError;
import oasis.names.tc.ebxml_regrep.xsd.rs._3.RegistryErrorList;

import us.hornerscorners.lamppost.exception.ServiceActionError;
import us.hornerscorners.lamppost.exception.ServiceActionException;
import us.hornerscorners.lamppost.exception.SeverityType;

import us.hornerscorners.lamppost.ihecommon.config.IHEConfig;

import us.hornerscorners.lamppost.model.audit.AuditContext;
import us.hornerscorners.lamppost.model.audit.CodedValueType;

import us.hornerscorners.lamppost.registry.constant.ErrorCode;
import us.hornerscorners.lamppost.registry.constant.ResponseStatus;

import us.hornerscorners.lamppost.registry.model.RegistryResponse;

import us.hornerscorners.lamppost.registry.transformer.xds
    .RegistryResponseTypeTransformer;

import us.hornerscorners.lamppost.repository.inject.RepositoryTarget;

import us.hornerscorners.lamppost.repository.service.IRepositoryRetrieveService;

import us.hornerscorners.lamppost.xca.model.RepositoryRequest;
import us.hornerscorners.lamppost.xca.model.RetrieveRequest;

import us.hornerscorners.lamppost.xca.transformer.IRetrieveTransformer;


/**
 * Class description
 *
 *
 * @version        v1.0, 2013-10-25
 * @author         Jim Horner
 */
public abstract class AbstractRetrieveService implements IRepositoryRetrieveService {

    /** Field description */
    @Inject
    private IHEConfig config;

    /** Field description */
    @Inject
    @RepositoryTarget
    private RegistryResponseTypeTransformer responseTypeTransformer;

    /**
     * Constructs ...
     *
     *
     */
    public AbstractRetrieveService() {

        super();
    }

    /**
     * Method description
     *
     *
     * @return
     */
    protected abstract CodedValueType getEventType();

    /**
     * Method description
     *
     *
     * @return
     */
    protected abstract IRetrieveTransformer getRequestTransformer();

    /**
     * Method description
     *
     *
     * @param body
     *
     * @return
     */
    @Override
    public RetrieveDocumentSetResponseType retrieveDocument(AuditContext auditContext,
            RetrieveDocumentSetRequestType body) {

        RetrieveDocumentSetResponseType result = new RetrieveDocumentSetResponseType();

        int successCount = 0;
        int errorCount = 0;
        int totalCount = 0;

        try {

            RetrieveRequest retrieveRequest = getRequestTransformer().transform(body);

            // initial population of response objects
            if (retrieveRequest.hasErrors()) {

                RegistryResponse regResp = new RegistryResponse(ResponseStatus.Failure,
                                               getClass(), retrieveRequest.getErrors());

                result.setRegistryResponse(
                    this.responseTypeTransformer.transform(regResp));

            } else {

                result.setRegistryResponse(
                    this.responseTypeTransformer.transform(ResponseStatus.Success));
            }

            AuditContext retrieveAuditContext = new AuditContext(getEventType(),
                                                    this.config.getClientConfig(),
                                                    auditContext.getTransactionContext(),
                                                    auditContext.getAddressContext());

            List<RegistryError> errors = new ArrayList<>();

            // TODO make multi-threaded
            for (RepositoryRequest repRequest : retrieveRequest.getRepositoryRequests()) {

                ++totalCount;

                IRepositoryRetrieveService client = repRequest.getRepositoryClient();
                RetrieveDocumentSetRequestType docSetRequest = repRequest.buildRequest();

                try {

                    RetrieveDocumentSetResponseType docSetResponse =
                        client.retrieveDocument(retrieveAuditContext, docSetRequest);

                    result.getDocumentResponse().addAll(
                        docSetResponse.getDocumentResponse());

                    RegistryErrorList docSetErrors =
                        docSetResponse.getRegistryResponse().getRegistryErrorList();

                    if (docSetErrors != null) {

                        errors.addAll(docSetErrors.getRegistryError());
                    }

                    String status = result.getRegistryResponse().getStatus();

                    if (ResponseStatus.Success.getCode().equals(status)) {
                        ++successCount;
                    } else if (ResponseStatus.Failure.getCode().equals(status)) {
                        ++errorCount;
                    }

                } catch (Exception e) {

                    ++errorCount;

                    String msg =
                        String.format(
                            "Unable to retrieve documents from repository [%s]; reason %s.",
                            repRequest.getRepositoryClient().getConfig().getId(),
                            e.getMessage());

                    ServiceActionError error =
                        new ServiceActionError(ErrorCode.XDSRepositoryError.getCode(),
                                               msg, SeverityType.Error);

                    errors.add(
                        this.responseTypeTransformer.createRegistryError(
                            getClass(), error));
                }
            }

            if (successCount == totalCount) {

                result.getRegistryResponse().setStatus(ResponseStatus.Success.getCode());

            } else if (errorCount == totalCount) {

                result.getRegistryResponse().setStatus(ResponseStatus.Failure.getCode());

            } else {

                result.getRegistryResponse().setStatus(
                    ResponseStatus.PartialSuccess.getCode());
            }

            if (errors.isEmpty() == false) {

                RegistryErrorList errorList =
                    result.getRegistryResponse().getRegistryErrorList();

                if (errorList == null) {

                    errorList = new RegistryErrorList();
                    result.getRegistryResponse().setRegistryErrorList(errorList);
                }

                errorList.getRegistryError().addAll(errors);
            }

        } catch (ServiceActionException e) {

            RegistryResponse regResp = new RegistryResponse(ResponseStatus.Failure, e);

            result.setRegistryResponse(this.responseTypeTransformer.transform(regResp));
        }

        return result;
    }
}
